/* 
 * File:   main.cpp
 * Author: Paul Stupka
 *
 * Created on October 22, 2011, 4:27 AM
 */

#include <cstdlib>
#include <unistd.h>
#include <sys/stat.h>
#include "HiddenFiles.h"
#include "ParsingParams.h"
#include <string>
#include <cstring>
#include <dirent.h>
#include <fnmatch.h>

std::string pathToProject;

void init_lkm();
std::string GetMajorNumber(char* lkm_Name);
std::set<std::string> FindMatches(char* dir, char* pattern, bool recursive);

int main(int argc, char* argv[]) {        
    pathToProject = argv[0];
    std::string binName = pathToProject.substr(pathToProject.rfind('/')-1);
    pathToProject = pathToProject.substr(0, pathToProject.rfind('/')+1);
    struct stat st;
    bool firstRun = false;
    if (stat("/sys/module/HideFiles",&st)!=0)
        firstRun = true;
    init_lkm();
    HiddenFiles hiddenFiles(pathToProject+"DataBase", firstRun);   
    if (argc != 1)
    {
        std::set<std::string> files;        
        Parameters params(argc, argv);
        char* filepath = params.FilePath();        
        char* pattern = params.Pattern();
        bool discardHiding = params.DiscardHiding();
        bool recursive = params.Recursive();        
        if (strcmp(filepath, "")==0)
        {
            std::cout<<"Specify a file or directory to hide.\n";
        }
        else
        {
            DIR* dp = opendir(filepath);            
            if ((filepath[strlen(filepath)-1]!='/')&&(dp!=NULL))
            {
                filepath = strcat(filepath,"/");
                closedir(dp);
            }
            if (discardHiding)    
            {
                files = hiddenFiles.FindMatches(filepath, pattern, recursive);
                system("sudo rmmod HideFiles");           
                init_lkm();       
                hiddenFiles.Remove(files);                
            }
            else
            {
                DIR* dp = opendir(filepath);
                if ((strlen(pattern)!=0)&&(dp!=NULL))
                {                
                    files = FindMatches(filepath, pattern, recursive);
                    closedir(dp);
                }
                else
                    files.insert(filepath);            
                hiddenFiles.Add(files);
            }
        }
    }
    if (stat("/dev/hidefile",&st)==0)
        system("sudo rm /dev/hidefile");
    return 0;
}

void init_lkm() {
    struct stat st;
    std::string command = "sudo insmod '" + pathToProject + "HideFiles.ko'";
    if (stat("/sys/module/HideFiles",&st)!=0)
        system(command.c_str());      
    if (stat("/dev/hidefile",&st)!=0)
    {        
        command = "sudo mknod /dev/hidefile c " + GetMajorNumber("Hide_Driver") + " 0";
        system(command.c_str());
        system("sudo chmod 777 /dev/hidefile");
    }
}

std::string GetMajorNumber(char* lkm_Name)
{
    std::fstream f_in("/proc/devices", std::ios::in);
    std::string str;
    std::string MajorNumber;
    f_in>>str>>str;        
    do
    {           
        f_in>>MajorNumber;
        f_in>>str;
    } while (str.find(lkm_Name)!=0);
    f_in.close();
    return MajorNumber;
}

std::set<std::string> FindMatches(char* dir, char* pattern, bool recursive)
{
    struct dirent* entry;
    std::set<std::string> files;
    DIR* dp = opendir(dir);
    while (entry = readdir(dp))
    {
        std::string stringBuilder = dir;                
        stringBuilder+=entry->d_name;    
        DIR* dp2;
        if ((dp2=opendir(stringBuilder.c_str())))
        {
            closedir(dp2);
            if (recursive)
            {
                if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0 )
                {                
                        std::string stringBuilder = dir;                
                        stringBuilder+=entry->d_name;
                        stringBuilder+="/";
                        std::set<std::string> files2;
                        files2=FindMatches((char*)stringBuilder.c_str(), pattern, recursive);
                        files.insert(files2.begin(),files2.end());
                }
            }
        }
        else
        {
            if (fnmatch(pattern, entry->d_name, FNM_NOESCAPE)==0)
            {
                std::string stringBuilder = dir;                
                stringBuilder+=entry->d_name;
                files.insert(stringBuilder);
            }
        }    
    }  
    closedir(dp);  
    return files;  
}